<!DOCTYPE html>
<html>
<head>
    

    

    



    <meta charset="utf-8">
    
    
    
    <title>redis | 尼古拉斯-鱼丸博客 | 喝酒、吃肉、不赌</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="redis">
    <meta name="description" content="Nosql系列产品之Redis什么是Redis?Redis是一个开源（BSD许可）的内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构，如字符串（strings），散列（hashes），列表（lists），集合（sets），有序集合（sorted sets）与范围查询，bitmaps，hyperloglogs和地理空间（geospatial）索引半径查询。Red">
<meta name="keywords" content="redis">
<meta property="og:type" content="article">
<meta property="og:title" content="redis">
<meta property="og:url" content="http://fish_08.oschina.io/2017/07/04/redis/index.html">
<meta property="og:site_name" content="尼古拉斯-鱼丸博客">
<meta property="og:description" content="Nosql系列产品之Redis什么是Redis?Redis是一个开源（BSD许可）的内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构，如字符串（strings），散列（hashes），列表（lists），集合（sets），有序集合（sorted sets）与范围查询，bitmaps，hyperloglogs和地理空间（geospatial）索引半径查询。Red">
<meta property="og:updated_time" content="2017-07-05T10:32:14.722Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="redis">
<meta name="twitter:description" content="Nosql系列产品之Redis什么是Redis?Redis是一个开源（BSD许可）的内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构，如字符串（strings），散列（hashes），列表（lists），集合（sets），有序集合（sorted sets）与范围查询，bitmaps，hyperloglogs和地理空间（geospatial）索引半径查询。Red">
    
        <link rel="alternative" href="/atom.xml" title="尼古拉斯-鱼丸博客" type="application/atom+xml">
    
    <link rel="shortcut icon" href="/favicon.ico">
    <link rel="stylesheet" href="/css/style.css?v=1.6.7">
    <script>window.lazyScripts=[]</script>
</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap">
      <div class="brand">
        <a href="/" class="avatar waves-effect waves-circle waves-light">
          <img src="/img/avatar.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">于亮</h5>
          <a href="mailto:136214289@qq.com" title="136214289@qq.com" class="mail">136214289@qq.com</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                文档
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                标签
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="http://git.oschina.net/fish_08" target="_blank" >
                <i class="icon icon-lg icon-github"></i>
                Github
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="http://weibo.com/2837335591/profile?rightmod=1&wvr=6&mod=personinfo" target="_blank" >
                <i class="icon icon-lg icon-weibo"></i>
                Weibo
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/custom"  >
                <i class="icon icon-lg icon-link"></i>
                测试
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">redis</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">redis</h1>
        <h5 class="subtitle">
            
                <time datetime="2017-07-04T07:15:38.000Z" itemprop="datePublished" class="page-time">
  2017-07-04
</time>


            
        </h5>
    </div>

    

</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#Nosql系列产品之Redis"><span class="post-toc-number">1.</span> <span class="post-toc-text">Nosql系列产品之Redis</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#什么是Redis"><span class="post-toc-number">1.1.</span> <span class="post-toc-text">什么是Redis?</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#Redis应用场景"><span class="post-toc-number">1.2.</span> <span class="post-toc-text">Redis应用场景</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#Redis为什么是单进程单线程模型？为什么那么快？"><span class="post-toc-number">1.3.</span> <span class="post-toc-text">Redis为什么是单进程单线程模型？为什么那么快？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#Redis的分布式实现？"><span class="post-toc-number">1.4.</span> <span class="post-toc-text">Redis的分布式实现？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#Redis的持久化机制？"><span class="post-toc-number">1.5.</span> <span class="post-toc-text">Redis的持久化机制？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#Redis和memcache对比？"><span class="post-toc-number">1.6.</span> <span class="post-toc-text">Redis和memcache对比？</span></a></li></ol></li></ol>
        </nav>
    </aside>
    
<article id="post-redis"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">redis</h1>
        <div class="post-meta">
            <time class="post-time" title="2017年07月04日 15:15" datetime="2017-07-04T07:15:38.000Z"  itemprop="datePublished">2017-07-04</time>

            


            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


            

        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <h2 id="Nosql系列产品之Redis"><a href="#Nosql系列产品之Redis" class="headerlink" title="Nosql系列产品之Redis"></a>Nosql系列产品之Redis</h2><h3 id="什么是Redis"><a href="#什么是Redis" class="headerlink" title="什么是Redis?"></a>什么是Redis?</h3><p>Redis是一个开源（BSD许可）的内存中的数据结构存储系统，它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构，如字符串（strings），散列（hashes），列表（lists），集合（sets），有序集合（sorted sets）与范围查询，bitmaps，hyperloglogs和地理空间（geospatial）索引半径查询。Redis内置了复制（replication），LUA脚本（Lua scripting），LRU驱动事件（LRU eviction），事务（transactions）和不同级别的 磁盘持久化（persistence）并通过 Redis哨兵Sentinel和自动分区（Cluster）提供高可用性（high availability）。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Redis作者宣言：	</div><div class="line">1.Redis是一个操作数据结构的语言工具，它提供基于TCP的协议以操作丰富的数据结构。在 Redis中数据结构这个词的意义不仅表示在某种数据结构上的操作，更包括了结构本身及这些操作的时间空间复制度。</div><div class="line">2.Redis 定位于一个内存数据库，正是由于内存的快速访问特性，才使得 Redis 能够有如此高的性能，才使得 Redis 能够轻松处理大量复杂的数据结构，Redis会尝试其它的存储方面的选择，但是永远不会改变它是一个内存数据库的角色。</div><div class="line">3.Redis 使用基础的API操作基础的数据结构，Redis 的 API 与数据结构一样，都是一些最基础的元素，你几乎可以将任何信息交互使用此 API 格式表示。作者调侃说，如果有其它非人类的智能生物存在，他们也能理解 Redis 的 API。因为它是如此的基础。</div><div class="line">4.Redis 有着诗一般优美的代码，经常有一些不太了解 Redis 原则的人会建议 Redis 采用一些其它人的代码，以实现一些 Redis 未实现的功能，但这对我们来说就像是非要给《红楼梦》接上后四十回一样。（作者此处用了莎士比亚的比喻）</div><div class="line">5.Redis 始终避免复杂化，我们认为设计一个系统的本质，就是与复杂化作战。我们不会为了一个小功能而往源码里添加上千行代码，解决复杂问题的方法就是让复杂问题永远不要提复杂的问题。</div><div class="line">6.Redis 支持两个层的 API，第一个层面包含部分操作 API，但它支持用于分布式环境下的 Redis。第二个层面的 API 支持更复杂的 multi-key 操作。它们各有所长，但是我们不会推出两者都支持的 API，但我们希望能够提供实例间数据迁移的命令，并执行 multi-key 操作。</div><div class="line">7.我们以优化代码为乐，我们相信编码是一件辛苦的工作，唯一对得起这辛苦的就是去享受它。如果我们在编码中失去了乐趣，那最好的解决办法就是停下来。我们决不会选择让 Redis 不好玩的开发模式。</div></pre></td></tr></table></figure>
<p><strong>1.Redis 与其他 key - value 缓存产品有以下三个特点?</strong><br> Redis支持数据的持久化，可以将内存中的数据保存在磁盘中，重启的时候可以再次加载进行使用。<br> Redis不仅仅支持简单的key-value类型的数据，同时还提供list，set，zset，hash等数据结构的存储。<br> Redis支持数据的备份，即master-slave模式的数据备份。</p>
<p><strong>2.Redis优势?</strong><br> 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。<br> 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。<br> 原子 – Redis的所有操作都是原子性的，同时Redis还支持对几个操作全并后的原子性执行。<br> 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。</p>
<p><strong>3.Redis与其他key-value存储有什么不同</strong><br>  Redis有着更为复杂的数据结构并且提供对他们的原子性操作，这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明， 无需进行额外的抽象。<br>  Redis运行在内存中但是可以持久化到磁盘，所以在对不同数据集进行高速读写时需要权衡内存，因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是，<br>  相比在磁盘上相同的复杂的数据结构，在内存中操作起来非常简单，这样Redis可以做很多内部复杂性很强的事情。同时，在磁盘格式方面他们是紧凑的以追加的方式产生的，<br>  因为他们并不需要进行随机访问。<br>链接: <a href="https://redis.io/" target="_blank" rel="external">Redis官网</a></p>
<h3 id="Redis应用场景"><a href="#Redis应用场景" class="headerlink" title="Redis应用场景"></a>Redis应用场景</h3><p>从数据结构分析应用场景:<br> <strong>1.Strings 数据结构是简单的key-value类型，value其实不仅是String，也可以是数字。</strong><br>    1.1.常规key-value缓存应用。<br>    1.2.常规计数: 微博数, 粉丝数</p>
<p><strong>2.Hashs它是一个String类型的field和value的映射表.</strong><br>    它的添加和删除都是平均的，hash特别适合用于存储对象，对于将对象存储成字符串而言，hash会占用更少的内存，并且可以更方便的存取整个对象. 它和java的HashMap完全类似.在Memcached中，我们经常将一些结构化的信息打包成hashmap,在客户端序列化后存储为一个字符串的值，比如用户的昵称、年龄、性别、积分等，这时候在需要修改其中某一项时，通常需要将所有值取出反序列化后，修改某一项的值，再序列化存储回去。这样不仅增大了开销，也不适用于一些可能并发操作的场合<br>（比如两个并发的操作都需要修改积分）。而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。<br>    2.1.存储部分变更数据  如用户信息等。</p>
<p><strong>3.Lists</strong><br>就是链表，略有数据结构知识的人都应该能理解其结构。使用Lists结构，我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列，可以利用Lists的PUSH操作，将任务存在Lists中，然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作Lists中某一段的api，你可以直接查询，删除Lists中某一段的元素。<br> Redis的list是每个子元素都是String类型的双向链表，可以通过push和pop操作从列表的头部或者尾部添加或者删除元素，这样List即可以作为栈，也可以作为队列。<br>     3.1消息队列系统<br>       使用list可以构建队列系统，使用sorted set甚至可以构建有优先级的队列系统。<br>       比如：将Redis用作日志收集器  实际上还是一个队列，多个端点将日志信息写入Redis，然后一个worker统一将所有日志写到磁盘。<br>     3.2.取最新N个数据的操作<br>       记录前N个最新登陆的用户Id列表，超出的范围可以从数据库中获得。<br>       //把当前登录人添加到链表里<br>       ret = r.lpush(“login:last_login_times”, uid)<br>       //保持链表只有N位<br>       ret = redis.ltrim(“login:last_login_times”, 0, N-1)<br>       //获得前N个最新登陆的用户Id列表<br>       last_login_list = r.lrange(“login:last_login_times”, 0, N-1)<br>       比如sina微博：<br>       在Redis中我们的最新微博ID使用了常驻缓存，这是一直更新的。但是我们做了限制不能超过5000个ID，因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候，才需要去访问数据库。<br>       我们的系统不会像传统方式那样“刷新”缓存，Redis实例中的信息永远是一致的。SQL数据库（或是硬盘上的其他类型数据库）只是在用户需要获取“很远”的数据时才会被触发，而主页或第一个评论页是不会麻烦到硬盘上的数据库了。<br>       比如典型的取你网站的最新文章，通过下面方式，我们可以将最新的5000条评论的ID放在Redis的List集合中，并将超出集合部分从数据库获取<br>       使用LPUSH latest.comments<id>命令，向list集合中插入数据<br>       插入完成后再用LTRIM latest.comments 0 5000命令使其永远只保存最近5000个ID<br>       然后我们在客户端获取某一页评论时可以用下面的逻辑（伪代码）</id></p>
<p><strong>4.Sets 就是一个集合，集合的概念就是一堆不重复值的组合。利用Redis提供的Sets数据结构，可以存储一些集合性的数据。</strong><br>     4.1.在微博应用中，可以将一个用户所有的关注人存在一个集合中，将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作，可以非常方便的实现如共同关注、共同喜好、二度好友等功能，对上面的所有集合操作，你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。<br>       Set是集合，是String类型的无序集合，set是通过hashtable实现的，概念和数学中个的集合基本类似，可以交集，并集，差集等等，set中的元素是没有顺序的。<br>     4.2.交集，并集，差集：(Set)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">//book表存储book名称</div><div class="line"><span class="built_in">set</span> book:1:name    ”The Ruby Programming Language”</div><div class="line"><span class="built_in">set</span> book:2:name     ”Ruby on rail”</div><div class="line"><span class="built_in">set</span> book:3:name     ”Programming Erlang”</div><div class="line">//tag表使用集合来存储数据，因为集合擅长求交集、并集</div><div class="line">sadd tag:ruby 1</div><div class="line">sadd tag:ruby 2</div><div class="line">sadd tag:web 2</div><div class="line">sadd tag:erlang 3</div><div class="line">//即属于ruby又属于web的书？</div><div class="line">inter_list = redis.sinter(<span class="string">"tag.web"</span>, <span class="string">"tag:ruby"</span>)</div><div class="line">//即属于ruby，但不属于web的书？</div><div class="line">inter_list = redis.sdiff(<span class="string">"tag.ruby"</span>, <span class="string">"tag:web"</span>)</div><div class="line">//属于ruby和属于web的书的合集？</div><div class="line">inter_list = redis.sunion(<span class="string">"tag.ruby"</span>, <span class="string">"tag:web"</span>)</div></pre></td></tr></table></figure></p>
<p>   4.3.获取某段时间所有数据去重值<br>这个使用Redis的set数据结构最合适了，只需要不断地将数据往set中扔就行了，set意为集合，所以会自动排重。<br>   4.4实时系统，反垃圾系统<br>      通过上面说到的set功能，你可以知道一个终端用户是否进行了某个操作，可以找到其操作的集合并进行分析统计对比等。没有做不到，只有想不到。</p>
<p><strong>5.Sorted Sets</strong><br>   和Sets相比，Sorted Sets增加了一个权重参数score，使得集合中的元素能够按score进行有序排列，比如一个存储全班同学成绩的Sorted Sets，其集合value可以是同学的学号，而score就可以是其考试得分，这样在数据插入集合的时候，就已经进行了天然的排序。可以用Sorted Sets来做带权重的队列，比如普通消息的score为1，重要消息的score为2，然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。<br>   5.1.排行榜应用，取TOP N操作<br>这个需求与上面需求的不同之处在于，前面操作以时间为权重，这个是以某个条件为权重，比如按顶的次数排序，这时候就需要我们的sorted set出马了，将你要排序的值设置成sorted set的score，将具体的数据设置成相应的value，每次只需要执行一条ZADD命令即可。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">//将登录次数和用户统一存储在一个sorted <span class="built_in">set</span>里</div><div class="line">zadd login:login_times 5 1</div><div class="line">zadd login:login_times 1 2</div><div class="line">zadd login:login_times 2 3</div><div class="line">ZADD key score member</div><div class="line">//当用户登录时，对该用户的登录次数自增1</div><div class="line">ret = r.zincrby(<span class="string">"login:login_times"</span>, 1, uid)</div><div class="line">//那么如何获得登录次数最多的用户呢，逆序排列取得排名前N的用户</div><div class="line">ret = r.zrevrange(<span class="string">"login:login_times"</span>, 0, N-1)</div><div class="line">ZREVRANGE key start stop [WITHSCORES]</div></pre></td></tr></table></figure></p>
<p>   5.2.在线游戏的排行榜，根据得分你通常想要：</p>
<pre><code>- 列出前100名高分选手
- 列出某用户当前的全球排名
</code></pre><p>这些操作对于Redis来说小菜一碟，即使你有几百万个用户，每分钟都会有几百万个新的得分。<br>模式是这样的，每次获得新得分时，我们用这样的代码：<br>ZADD leaderboard <score> <username><br>你可能用userID来取代username，这取决于你是怎么设计的。<br>得到前100名高分用户很简单：ZREVRANGE leaderboard 0 99。<br>用户的全球排名也相似，只需要：ZRANK leaderboard <username>。<br>ZRANK key member<br>Determine the index of a member in a sorted set<br>   5.3.需要精准设定过期时间的应用<br>比如你可以把上面说到的sorted set的score值设置成过期时间的时间戳，那么就可以简单地通过过期时间排序，定时清除过期数据了，不仅是清除Redis中的过期数据，你完全可以把Redis里这个过期时间当成是对数据库中数据的索引，用Redis来找出哪些数据需要过期删除，然后再精准地从数据库中删除相应的记录。<br>   5.4.范围查找<br>比如：有一个IP范围对应地址的列表，现在需要给出一个IP的情况下，迅速的查找到这个IP在哪个范围，也就是要判断此IP的所有地。<br>例如：查询IP是否存在的问题；<br>ADSM，查询IP是否在其他分组中存在。写json文件<br>sadd向名称为key的set中添加元素，返回影响元素的个数，0为失败，1为成功<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">例如：有下面两个范围，10－20和30－40</div><div class="line">A_start 10, A_end 20</div><div class="line">B_start 30, B_end 40</div><div class="line">我们将这两个范围的起始位置存在Redis的Sorted Sets数据结构中，基本范围起始值作为score，范围名加start和end为其value值：</div><div class="line">redis 127.0.0.1:6379&gt; zadd ranges 10 A_start</div><div class="line">(<span class="built_in">integer</span>) 1</div><div class="line">redis 127.0.0.1:6379&gt; zadd ranges 20 A_end</div><div class="line">(<span class="built_in">integer</span>) 1</div><div class="line">redis 127.0.0.1:6379&gt; zadd ranges 30 B_start</div><div class="line">(<span class="built_in">integer</span>) 1</div><div class="line">redis 127.0.0.1:6379&gt; zadd ranges 40 B_end</div><div class="line">(<span class="built_in">integer</span>) 1</div><div class="line">这样数据在插入Sorted Sets后，相当于是将这些起始位置按顺序排列好了。</div><div class="line">现在我需要查找15这个值在哪一个范围中，只需要进行如下的zrangbyscore查找：</div><div class="line">redis 127.0.0.1:6379&gt; zrangebyscore ranges (15 +inf LIMIT 0 1</div><div class="line">1) <span class="string">"A_end"</span></div><div class="line">这个命令的意思是在Sorted Sets中查找大于15的第一个值。（+inf在Redis中表示正无穷大，15前面的括号表示&gt;15而非&gt;=15）</div><div class="line">查找的结果是A_end，由于所有值是按顺序排列的，所以可以判定15是在A_start到A_end区间上，也就是说15是在A这个范围里。至此大功告成。</div><div class="line">当然，如果你查找到的是一个start，比如咱们用25，执行下面的命令</div><div class="line">redis 127.0.0.1:6379&gt; zrangebyscore ranges (25 +inf LIMIT 0 1</div><div class="line">1) <span class="string">"B_start"</span></div><div class="line">返回结果表明其下一个节点是一个start节点，也就是说25这个值不处在任何start和end之间，不属于任何范围。</div></pre></td></tr></table></figure></username></username></score></p>
<p>   5.5.构建有优先级队列系统<br>     使用sorted set甚至可以构建有优先级的队列系统。</p>
<p><strong>6.计数器应用</strong><br>      Redis的命令都是原子性的，你可以轻松地利用INCR，DECR命令来构建计数器系统</p>
<p><strong>7.Pub/Sub构建实时消息系统</strong><br>  Redis的Pub/Sub系统可以构建实时的消息系统，比如很多用Pub/Sub构建的实时聊天系统的例子。<br> Pub/Sub 从字面上理解就是发布（Publish）与订阅（Subscribe），在Redis中，你可以设定对某一个key值进行消息发布及消息订阅，当一个key值上进行了消息发布后，所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统，比如普通的即时聊天，群聊等功能。<br> 1.Qlocenter 下发策略<br> 2.Pub/Sub构建实时消息系统<br> Redis的Pub/Sub系统可以构建实时的消息系统<br> 比如很多用Pub/Sub构建的实时聊天系统的例子</p>
<p>实例：<br>A.抢XXX赠券、抽奖系统的奖品库存，使用的Redis中的链表<br>前一天晚上通过定时服务推送奖品库存，使用LPUSH命令将乱序的奖品推入List中，抽奖时则调用LPOP命令，将最左侧奖品弹出队列，提示用户中奖。同时，发送异步消息，让消息去处理中奖纪录并插入关系型数据库中。<br>好处：<br>出队操作速度极快，可以满足多人并发抽奖的场景。<br>使用了消息队列，避免了数据库并发操作。<br>B.某活动累计充值xxx元，奖励xxx。使用Redis中的string/hash（哈希）结构<br>用户每次充值，都发送一个充值MQ事件（使用RabbitMQ），另一个程序，消费充值MQ事件，将事件中的用户ID、充值金额分别存到Redis（string/hash）里面。<br>以后，就可以直接汇总用户总充值金额给满足条件的客户赠送奖品。<br>好处：<br>完全避免了关系性数据库的查询插入操作<br>Redis的查询速度非常快，提升了用户体验</p>
<h3 id="Redis为什么是单进程单线程模型？为什么那么快？"><a href="#Redis为什么是单进程单线程模型？为什么那么快？" class="headerlink" title="Redis为什么是单进程单线程模型？为什么那么快？"></a>Redis为什么是单进程单线程模型？为什么那么快？</h3><p>  官方提供数据: 100000+的qps<br>快速原因：<br><strong>1.完全基于内存的</strong><br>     顺序访问内存访问比硬盘快6-7倍<br>     随机访问内存访问比硬盘快10万倍<br>     本质原因，取决于pc对存储设备需求： 1.访问快 2.永久存储  3.容量大<br>     目前没有存储设备同时满足以上三种需求，因此pc结构将上面分为两个部分，内部存储器负责高速传输，外部存储器负责大容量的长期保存。内存是cpu直接通过地址和数据总线访问的，它是用来储存cpu常用数据的。cpu会频繁的访问内存，所以内存必须拥有足够高的读写速度，才不会成为系统性能的瓶颈。硬盘是通过串行的方式访问的，它的用途就是永久性的储存数据，速度快不快，一定程度上对系统性能的影响不像内存那么明显。当cpu想要访问硬盘中的数据，只能先把他们读到内存中，也就是加载到内存，然后cpu再访问内存以达到读取数据的目的。也就是说，硬盘的速度慢一些，影响到操作有，加载、传输文件，加载应用程序。一旦相应的数据到了内存中，硬盘的速度就无关紧要了。所以，硬盘的访问频率相比内存来讲并不是相高。另外，内存有一个特点是掉点易失，也就是pc断电之后内存中的数据就全部丢失了，这也是为什电脑突然断电之后再次开启不是上一次的进度（因为上一次的进度存储在内存中，内存掉点数据就没了）。正因为它可以掉电不保存数据，所以才有可能在一定的成本下中达到如此高的速度。而硬盘要求的是稳定和大容量，同时掉电不易失是基本要求。在掉电不易失的要求下，硬盘能选择的原理就不那么多了，至少内存颗粒的原理就不适用。所以现在硬盘进化到了利用巨磁效应的地步，而下一种技术是flash颗粒，也就是ssd所采用的解决方案。<br>     作者：故纸<br>     链接：<a href="https://www.zhihu.com/question/32282451/answer/130873815" target="_blank" rel="external">https://www.zhihu.com/question/32282451/answer/130873815</a><br>     来源：知乎</p>
<p><strong>2. 多路 I/O 复用技术，并发高</strong><br>2.1 缓存 I/O<br>   缓存 I/O 又被称作标准 I/O，大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中，操作系统会将 I/O 的数据缓存在文件系统的页缓存（ page cache ）中，也就是说，数据会先被拷贝到操作系统内核的缓冲区中，然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。<br>   缓存 I/O 的缺点：数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作，这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。<br>   2.2 IO模式<br>   对于一次IO访问（以read举例），数据会先被拷贝到操作系统内核的缓冲区中，然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说，当一个read操作发生时，它会经历两个阶段：</p>
<pre><code>  1. 等待数据准备 (Waiting for the data to be ready)
  2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
  正式因为这两个阶段，linux系统产生了下面五种网络模式的方案。
- 阻塞 I/O（blocking IO）
- 非阻塞 I/O（nonblocking IO）
- I/O 多路复用（ IO multiplexing）
- 信号驱动 I/O（ signal driven IO）
- 异步 I/O（asynchronous IO）
注：由于signal driven IO在实际中并不常用，所以我这只提及剩下的四种IO Model。
IO multiplexing就是我们说的select，poll，epoll，有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select，poll，epoll这个function会不断的轮询所负责的所有socket，当某个socket有数据到达了，就通知用户进程。
  3.单个 Redis 进程没办法使用多核，多开进程
    当用户进程调用了select，那么整个进程会被block，而同时，kernel会“监视”所有select负责的socket，当任何一个socket中的数据准备好了，select就会返回。这个时候用户进程再调用read操作，将数据从kernel拷贝到用户进程。
所以，I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符，而这些文件描述符（套接字描述符）其中的任意一个进入读就绪状态，select()函数就可以返回。
这个图和blocking IO的图其实并没有太大的不同，事实上，还更差一些。因为这里需要使用两个system call (select 和 recvfrom)，而blocking IO只调用了一个system call (recvfrom)。但是，用select的优势在于它可以同时处理多个connection。
所以，如果处理的连接数不是很高的话，使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好，可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快，而是在于能处理更多的连接。）
 在IO multiplexing Model中，实际中，对于每一个socket，一般都设置成为non-blocking，但是，如上图所示，整个用户的process其实是一直被block的。只不过process是被select这个函数block，而不是被socket IO给block。
</code></pre><p><strong>3.单进程单线程好处</strong><br>    1.代码更清晰，处理逻辑更简单<br>    2.不用去考虑各种锁的问题，不存在加锁释放锁操作，没有因为可能出现死锁而导致的性能消耗<br>    3.不存在多进程或者多线程导致的切换而消耗CPU<br>    4.非阻塞IO<br>    内部实现采用epoll，采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件，然后利用epoll的多路复用特性，绝不在io上浪费一点时间 这3个条件不是相互独立的，特别是第一条，如果请求都是耗时的，采用单线程吞吐量及性能可想而知了。应该说redis为特殊的场景选择了合适的技术方案。<br>        作者：申仕杰<br>        链接：<a href="https://www.zhihu.com/question/55818031/answer/153320457" target="_blank" rel="external">https://www.zhihu.com/question/55818031/answer/153320457</a><br>        来源：知乎<br>    多路I/O 复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力，在空闲的时候，会把当前线程阻塞掉，当有一个或多个流有I/O事件时，就从阻塞态中唤醒，于是程序就会轮询一遍所有的流（epoll是只轮询那些真正发出了事件的流），并且只依次顺序的处理就绪的流，这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接，“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求（尽量减少网络IO的时间消耗），且Redis在内存中操作数据的速度非常快（内存内的操作不会成为这里的性能瓶颈），主要以上两点造就了Redis具有很高的吞吐量。<br>    和Memcached不同，Redis并没有直接使用Libevent，而是自己完成了一个非常轻量级的对select、epoll、evport、kqueue这些通用的接口的实现。在不同的系统调用选用适合的接口，linux下默认是epoll。因为Libevent比较重更通用代码量也就很庞大，拥有很多Redis用不上的功能，Redis为了追求“轻巧”并且去除依赖，就选择自己去封装了一套<br>多线程中操作，那就需要为这些对象加锁。所以使用多线程可以提高性能，但是每个线程的效率严重下降了，而且程序的逻辑严重复杂化。Redis的数据结构并不全是简单的Key-Value，还有list，hash等复杂的结构，这些结构有可能会进行很细粒度的操作，比如在很长的列表后面添加一个元素，在hash当中添加或者删除一个对象，这些操作还可以合成MULTI/EXEC的组。这样一个操作中可能就需要加非常多的锁，导致的结果是同步开销大大增加。Redis在权衡之后的选择是用单线程，突出自己功能的灵活性。在单线程基础上任何原子操作都可以几乎无代价地实现，多么复杂的数据结构都可以轻松运用，甚至可以使用Lua脚本这样的功能。对于多线程来说这需要高得多的代价。并不是所有的KV数据库或者内存数据库都应该用单线程，比如ZooKeeper就是多线程的，最终还是看作者自己的意愿和取舍。单线程的威力实际上非常强大，每核心效率也非常高，在今天的虚拟化环境当中可以充分利用云化环境来提高资源利用率。多线程自然是可以比单线程有更高的性能上限，但是在今天的计算环境中，即使是单机多线程的上限也往往不能满足需要了，需要进一步摸索的是多服务器集群化的方案，这些方案中多线程的技术照样是用不上的，所以单线程、多进程的集群不失为一个时髦的解决方案。<br><strong>4.单进程单线程弊端</strong><br>    无法发挥多核CPU性能，不过可以通过在单机开多个Redis实例来完善；</p>
<h3 id="Redis的分布式实现？"><a href="#Redis的分布式实现？" class="headerlink" title="Redis的分布式实现？"></a>Redis的分布式实现？</h3><p>3.0版本之前不支持集群，需要自己实现一套算法获知存取节点，节点无法感知，节点负载均衡不好。<br>高性能同时，实现水平扩展，文档称可以水平扩展到1000节点，高可用特性。<br>  Consistency(一致性), 数据一致更新，所有数据变动都是同步的<br>  Availability(可用性), 好的响应性能<br>  Partition tolerance(分区容错性) 可靠性<br>  定理：任何分布式系统只可同时满足二点，没法三者兼顾。<br>  有了Cluster功能后，Redis从一个单纯的NoSQL内存数据库变成了分布式NoSQL数据库，CAP模型也从CP变成了AP。也就是说，通过自动分片和冗余数据，Redis具有了真正的分布式能力，某个结点挂了的话，因为数据在其他结点上有备份，所以其他结点顶上来就可以继续提供服务，保证了Availability。然而，也正因为这一点，Redis无法保证曾经的强一致性了。这也是CAP理论要求的，三者只能取其二</p>
<p>Redis 集群的优势:<br>自动分割数据到不同的节点上。<br>整个集群的部分节点失败或者不可达的情况下能够继续处理命令。</p>
<p><strong>1.Redis 集群的数据分片</strong><br>Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.<br>Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:<br>节点 A 包含 0 到 5500号哈希槽.<br>节点 B 包含5501 到 11000 号哈希槽.<br>节点 C 包含11001 到 16384号哈希槽.<br>这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.<br><strong>2.Redis 集群的主从复制模型</strong><br>为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用，所以集群使用了主从复制模型,每个节点都会有N-1个复制品.<br>在我们例子中具有A，B，C三个节点的集群,在没有复制模型的情况下,如果节点B失败了，那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.<br>然而如果在集群创建的时候（或者过一段时间）我们为每个节点添加一个从节点A1，B1，C1,那么整个集群便有三个master节点和三个slave节点组成，这样在节点B失败后，集群便会选举B1为新的主节点继续服务，整个集群便不会因为槽找不到而不可用了<br>不过当B和B1 都失败后，集群是不可用的.<br><strong>3.Redis 一致性保证</strong><br>Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作.<br>第一个原因是因为集群是用了异步复制. 写操作过程:<br>客户端向主节点B写入一条命令.<br>主节点B向客户端回复命令状态.<br>主节点将写操作复制给他得从节点 B1, B2 和 B3.<br>主节点对命令的复制工作发生在返回命令回复之后， 因为如果每次处理命令请求都需要等待复制操作完成的话， 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。 注意：Redis 集群可能会在将来提供同步写的方法。 Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区， 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。<br>举个例子 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点， 其中 A 、B 、C 为主节点， A1 、B1 、C1 为A，B，C的从节点， 还有一个客户端 Z1 假设集群中发生网络分区，那么集群可能会分为两方，大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ，小部分的一方则包含节点 B 和客户端 Z1 .<br>Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的master，那么Z1写入B中得数据便丢失了.<br>注意， 在网络分裂出现期间， 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的， 这一时间限制称为节点超时时间（node timeout）， 是 Redis 集群的一个重要的配置选项</p>
<h3 id="Redis的持久化机制？"><a href="#Redis的持久化机制？" class="headerlink" title="Redis的持久化机制？"></a>Redis的持久化机制？</h3><p><strong>1.RDB持久化优势和劣势</strong><br>    既然快照是保存某一个时点的内存数据，那么就要求在进行快照时内存数据不能发生变化。关于这点Redis有两种模式：SAVE和BGSAVE。SAVE模式首先使Redis停止服务，然后将这个时点的Redis内存数据写入到硬盘中，等完成写入后Redis开始提供服务，这个过程一般会比较长，在生产环境中使用的场景较少。BGSAVE模式会使用fork命令拷贝Redis的内存，在fork过程中Redis也会停止对外服务，但这个过程非常快，在实体机或者VMWare和KVM中1G的内存拷贝大概在10-20ms，而在Xen中要稍慢些，大概在200-300ms。拷贝内存完成后，拷贝的这份内存数据开始持久化到硬盘，同时Redis也继续对外提供服务。SAVE模式对Redis可用性影响很大，可能使Redis在一段较长时间内无法提供服务。BGSAVE模式对Redis可用性影响较小，1G的内存数据会使Redis中断服务10-20ms，这个数值在一般情况下是可以接受的，但是因为BGSAVE是使用fork机制，这就要求fork时系统的可用内存至少要和Redis占用的内存一样大。<br>    RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。<br>　　也是默认的持久化方式，这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。<br>    可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照，下面是默认的快照保存配置<br>    save 900 1     #900秒内如果超过1个key被修改，则发起快照保存<br>    save 300 10    #300秒内容如超过10个key被修改，则发起快照保存<br>    save 60 10000<br>    RDB文件保存过程<br>    redis调用fork,现在有了子进程和父进程。<br>    父进程继续处理client请求，子进程负责将内存内容写入到临时文件。由于os的写时复制机制（copy on write)父子进程会共享相同的物理页面，当父进程处理写请求时os会为父进程要修改的页面创建副本，而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。<br>    当子进程将快照写入临时文件完毕后，用临时文件替换原来的快照文件，然后子进程退出。<br>    client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的，由于redis是用一个主线程来处理所有 client的请求，这种方式会阻塞所有client请求。所以不推荐使用。<br>    另一点需要注意的是，每次快照持久化都是将内存数据完整写入到磁盘一次，并不 是增量的只同步脏数据。如果数据量大的话，而且写操作比较多，必然会引起大量的磁盘io操作，可能会严重影响性能。<br>    优势<br>    一旦采用该方式，那么你的整个Redis数据库将只包含一个文件，这样非常方便进行备份。比如你可能打算没1天归档一些数据。<br>    方便备份，我们可以很容易的将一个一个RDB文件移动到其他的存储介质上<br>    RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。<br>    RDB 可以最大化 Redis 的性能：父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程，然后这个子进程就会处理接下来的所有保存工作，父进程无须执行任何磁盘 I/O 操作。<br>    劣势<br>    如果你需要尽量避免在服务器故障时丢失数据，那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点（save point）来控制保存 RDB 文件的频率， 但是， 因为RDB 文件需要保存整个数据集的状态， 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下， 一旦发生故障停机， 你就可能会丢失好几分钟的数据。<br>    每次保存 RDB 的时候，Redis 都要 fork() 出一个子进程，并由子进程来进行实际的持久化工作。 在数据集比较庞大时， fork() 可能会非常耗时，造成服务器在某某毫秒内停止处理客户端； 如果数据集非常巨大，并且 CPU 时间非常紧张的话，那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ，但无论 AOF 重写的执行间隔有多长，数据的耐久性都不会有任何损失。</p>
<p><strong>2.AOF持久化优势和劣势</strong><br>   AOF方式将Redis每次的写操作都保存在日志文件中，通过回放日志文件Redis即可恢复所有的数据。Redis将每次写操作的日志都保存在内存的一个缓存区中而不是即时写入硬盘，Redis提供了三种方式将内存日志同步到硬盘。<br>   文件同步方式<br>always : Redis在每次有写操作发生时都会同步到硬盘，这种方式会给IO带来很大压力，实际中写入的速率要考虑硬盘的IO速率。<br>everysec :Redis每1秒将日志同步到硬盘，这种方式性能较好，但也意为着如果Redis崩溃那么将丢失1秒的数据。<br>no : Redis不主动控制内存缓存区和硬盘的同步，而由操作系统来控制。这种方式不可控，一般不采用。<br>Rewriting/compacting AOF<br>AOF方式各方面比较均衡，但是有个缺点：随着Redis的运行硬盘上的日志文件将会越来越大，将会占用大量硬盘空间，而且在Redis重启时也会因为日志文件太大而需要很长的启动时间。为了解决这个问题Redis提供了BGREWRITEAOF命令来重建日志。<br>BGREWRITEAOF运行机制<br>BGREWRITEAOF运行机制和BGSAVE很类似：使用fork创建拷贝进程，然后再将拷贝内存中的数据写入到临时的日志文件中，完成写入后再用rename原子操作替换掉日志文件。<br>BGREWRITEAOF存在的问题<br>BGREWRITEAOF同BGSAVE一样有着以下问题：<br>fork时要暂停Redis对外服务<br>需要系统为fork保留内存空间<br>除此之外，BGREWRITEAOF还有这自己的问题：<br>因为要替换日志文件，如果日志文件非常大，比如几十G，那么替换日志文件的时间也会比较长。</p>
<p>分析了Redis的快照和AOF持久机制，在实际生产环境中部署该选择哪种方式呢？下面从Redis可用性影响、数据丢失、内存占用、IO负荷、硬盘空间占用几个维度比较各持久化方式：<br>从上表可以看出AOF或者AOF+BGREWRITEAOF方式在一般场景下应该是优先考虑的持久化方式。 </p>
<p><strong>3.持久化机制的选择</strong><br>  当业务不需要数据持久化时，当然关闭所有的持久化方式可以获得最佳的性能以及最大内存的使用量；如果需要使用持久化，根据是否可以容忍重启丢失部分数据在快照方式与语句追加方式之间选择其一，不要使用虚拟内存以及diskstore方式。目前来说，个人觉得选择aof的方式是最佳的选择。<br>  难以取舍的持久化机制主要是快照和aof方式。相比于aof快照的特点是速度快，恢复的速度也快。但是宕机的时候丢失的数据相对多一点。<br>当aof和快照同时开启的时候。<br>数据恢复时会首先使用 aof的文件来恢复，恢复失败的时候再去考虑快照。<br>redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。<br>当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改，所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。有三种方式如下（默认是：每秒fsync一次）<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">appendonly yes              //启用aof持久化方式</div><div class="line">//appendfsync always        每次收到写命令就立即强制写入磁盘，最慢的，但是保证完全的持久化，不推荐使用</div><div class="line">appendfsync everysec     //每秒钟强制写入磁盘一次，在性能和持久化方面做了很好的折中，推荐</div><div class="line">//appendfsync no     完全依赖os，性能最好,持久化没保证</div></pre></td></tr></table></figure></p>
<p>aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次，文件中必须保存全部的100条命令，其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。<br>为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中，最后替换原来的文件。具体过程如下<br>redis调用fork ，现在有父子两个进程<br>子进程根据内存中的数据库快照，往临时文件中写入重建数据库状态的命令<br>父进程继续处理client请求，除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。<br>当子进程把快照内容写入已命令方式写到临时文件中后，子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。<br>现在父进程可以使用临时文件替换老的aof文件，并重命名，后面收到的写命令也开始往新的aof文件中追加。<br>需要注意到是重写aof文件的操作，并没有读取旧的aof文件，而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。<br><strong>优势</strong><br>使用 AOF 持久化会让 Redis 变得非常耐久（much more durable）：你可以设置不同的 fsync 策略，比如无 fsync ，每秒钟一次 fsync ，或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次，在这种配置下，Redis 仍然可以保持良好的性能，并且就算发生故障停机，也最多只会丢失一秒钟的数据（ fsync 会在后台线程执行，所以主线程可以继续努力地处理命令请求）。<br>AOF 文件是一个只进行追加操作的日志文件（append only log）， 因此对 AOF 文件的写入不需要进行 seek ， 即使日志因为某些原因而包含了未写入完整的命令（比如写入时磁盘已满，写入中途停机，等等）， redis-check-aof 工具也可以轻易地修复这种问题。<br>Redis 可以在 AOF 文件体积变得过大时，自动地在后台对 AOF 进行重写： 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的，因为 Redis 在创建新 AOF 文件的过程中，会继续将命令追加到现有的 AOF 文件里面，即使重写过程中发生停机，现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕，Redis 就会从旧 AOF 文件切换到新 AOF 文件，并开始对新 AOF 文件进行追加操作。<br>AOF 文件有序地保存了对数据库执行的所有写入操作， 这些写入操作以 Redis 协议的格式保存， 因此 AOF 文件的内容非常容易被人读懂， 对文件进行分析（parse）也很轻松。 导出（export） AOF 文件也非常简单： 举个例子， 如果你不小心执行了 FLUSHALL 命令， 但只要 AOF 文件未被重写， 那么只要停止服务器， 移除 AOF 文件末尾的 FLUSHALL 命令， 并重启 Redis ， 就可以将数据集恢复到 FLUSHALL 执行之前的状态。<br><strong>劣势</strong><br>对于相同的数据集来说，AOF 文件的体积通常要大于 RDB 文件的体积。<br>根据所使用的 fsync 策略，AOF 的速度可能会慢于 RDB 。 在一般情况下， 每秒 fsync 的性能依然非常高， 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快， 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时，RDB 可以提供更有保证的最大延迟时间（latency）。<br>AOF 在过去曾经发生过这样的 bug ： 因为个别命令的原因，导致 AOF 文件在重新载入时，无法将数据集恢复成保存时的原样。 （举个例子，阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。） 测试套件里为这种情况添加了测试： 它们会自动生成随机的、复杂的数据集， 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见， 但是对比来说， RDB 几乎是不可能出现这种 bug 的。<br><strong>抉择</strong><br>一般来说， 如果想达到足以媲美 PostgreSQL 的数据安全性， 你应该同时使用两种持久化功能。<br>如果你非常关心你的数据， 但仍然可以承受数分钟以内的数据丢失， 那么你可以只使用 RDB 持久化。<br>其余情况我个人喜好选择AOF<br>appendonly yes         //启用 aof 持久化方式（默认是：每秒 fsync 一次）<br>appendfsync always    //收到写命令就立即写入磁盘，最慢，但是保证完全的持久化<br>appendfsync everysec   //每秒钟写入磁盘一次，在性能和持久化方面做了很好的折中<br>appendfsync no      //完全依赖os，性能最好,持久化没保证<br>aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test 命令 100 次，文件中必须保存全部的 100 条命令，其实有 99 条都是多余的。因为要恢复数 据库的状态其实文件中保存一条 set test 100 就够了。为了压缩 aof 的持久化文件。redis 提 供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令 的方式保存到临时文件中，最后替换原来的文件。具体过程如下：<br>1、redis 调用 fork ，现在有父子两个进程<br>2、子进程根据内存中的数据库快照，往临时文件中写入重建数据库状态的命令<br>3、父进程继续处理 client 请求，除了把写命令写入到原来的 aof 文件中。同时把收到的写命 令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题；<br>4、当子进程把快照内容写入已命令方式写到临时文件中后，子进程发信号通知父进程。然 后父进程把缓存的写命令也写入到临时文件；<br>5、现在父进程可以使用临时文件替换老的 aof 文件，并重命名，后面收到的写命令也开始 往新的 aof 文件中追加。<br>需要注意到是重写 aof 文件的操作，并没有读取旧的 aof 文件，而是将整个内存中的数据库 内容用命令的方式重写了一个新的 aof 文件,这点和快照有点类似。</p>
<h3 id="Redis和memcache对比？"><a href="#Redis和memcache对比？" class="headerlink" title="Redis和memcache对比？"></a>Redis和memcache对比？</h3><pre><code>Redis单个value最大512 Memcahce最大value是1M
1. Redis中，并不是所有的数据都一直存储在内存中的，这是和Memcached相比一个最大的区别。
2. redis不仅仅支持简单的k/v类型的数据，同时还提供list，set，hash等数据结构的存储。
3. Redis支持数据的备份，即master-slave模式的数据备份。
4. Redis支持数据的持久化，可以将内存中的数据保持在磁盘中，重启的时候可以再次加载进行使用。
Redis在很多方面具备数据库的特征，或者说就是一个数据库系统，而Memcached只是简单的K/V缓存
</code></pre><p>   没有必要过多的关注性能。由于Redis只使用单核，而Memcached可以使用多核，所以在比较上，平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中，Memcached性能要高于Redis，虽然Redis最近也在存储大数据的性能上进行优化，但是比起Memcached，还是稍有逊色。说了这么多，结论是，无论你使用哪一个，每秒处理请求的次数都不会成为瓶颈。<br>   你需要关注内存使用率。对于key-value这样简单的数据储存，memcache的内存使用率更高。如果采用hash结构，redis的内存使用率会更高。当然，这些都依赖于具体的应用场景。<br>   你需要关注关注数据持久化和主从复制时，只有redis拥有这两个特性。如果你的目标是构建一个缓存在升级或者重启后之前的数据不会丢失的话，那也只能选择redis。<br>   你应该关心你需要的操作。redis支持很多复杂的操作，甚至只考虑内存的使用情况，在一个单一操作里你常常可以做很多，而不需要将数据读取到客户端中（这样会需要很多的IO操作）。这些复杂的操作基本上和纯GET和POST操作一样快，所以你不只是需要GET/SET而是更多的操作时，redis会起很大的作用。<br>   对于两者的选择还是要看具体的应用场景，如果需要缓存的数据只是key-value这样简单的结构时，我在项目里还是采用memcache，它也足够的稳定可靠。如果涉及到存储，排序等一系列复杂的操作时，毫无疑问选择redis。<br>   关于redis和memcache的不同，下面罗列了一些相关说法，供记录：<br>   redis和memecache的不同在于[2]：<br>   1、存储方式：<br>   memecache 把数据全部存在内存之中，断电后会挂掉，数据不能超过内存大小<br>   redis有部份存在硬盘上，这样能保证数据的持久性，支持数据的持久化（笔者注：有快照和AOF日志两种持久化方式，在实际应用的时候，要特别注意配置文件快照参数，要不就很有可能服务器频繁满载做dump）。<br>   2、数据支持类型：<br>   redis在数据支持上要比memecache多的多。<br>   3、使用底层模型不同：<br>   新版本的redis直接自己构建了VM 机制 ，因为一般的系统调用系统函数的话，会浪费一定的时间去移动和请求。<br>   4、运行环境不同：<br>   redis目前官方只支持Linux 上去行，从而省去了对于其它系统的支持，这样的话可以更好的把精力用于本系统 环境上的优化，虽然后来微软有一个小组为其写了补丁。但是没有放到主干上<br>   个人总结一下，有持久化需求或者对数据结构和处理有高级要求的应用，选择redis，其他简单的key/value存储，选择memcache。</p>

        </div>

        <blockquote class="post-copyright">
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2017-07-05T10:32:14.722Z" itemprop="dateUpdated">2017年7月5日 18:32</time>
</span><br>


        如要转载请注明出处：<a href="/2017/07/04/redis/" target="_blank" rel="external">http://fish_08.oschina.io/2017/07/04/redis/</a>
    </div>
    <footer>
        <a href="http://fish_08.oschina.io">
            <img src="/img/avatar.jpg" alt="于亮">
            于亮
        </a>
    </footer>
</blockquote>

        
<div class="page-reward">
    <a id="rewardBtn" href="javascript:;" class="page-reward-btn waves-effect waves-circle waves-light">赏</a>
</div>



        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/redis/">redis</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://fish_08.oschina.io/2017/07/04/redis/&title=《redis》 — 尼古拉斯-鱼丸博客&pic=http://fish_08.oschina.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://fish_08.oschina.io/2017/07/04/redis/&title=《redis》 — 尼古拉斯-鱼丸博客&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://fish_08.oschina.io/2017/07/04/redis/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《redis》 — 尼古拉斯-鱼丸博客&url=http://fish_08.oschina.io/2017/07/04/redis/&via=http://fish_08.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://fish_08.oschina.io/2017/07/04/redis/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between">
  
    <div class="waves-block waves-effect prev">
      <a href="/2017/07/06/lucene/" id="post-prev" class="post-nav-link">
        <div class="tips"><i class="icon icon-angle-left icon-lg icon-pr"></i> Prev</div>
        <h4 class="title">lucene</h4>
      </a>
    </div>
  

  
    <div class="waves-block waves-effect next">
      <a href="/2017/06/28/quartz/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">Quartz</h4>
      </a>
    </div>
  
</nav>



    







</article>

<div id="reward" class="page-modal reward-lay">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <h3 class="reward-title">
        <i class="icon icon-quote-left"></i>
        谢谢包子~
        <i class="icon icon-quote-right"></i>
    </h3>
    <ul class="reward-items">
        
        <li>
            <img src="/img/wechat.png" title="微信打赏二维码" alt="微信打赏二维码">
            <p>微信</p>
        </li>
        

        
        <li>
            <img src="/img/alipay.png" title="支付宝打赏二维码" alt="支付宝打赏二维码">
            <p>支付宝</p>
        </li>
        
    </ul>
</div>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            <span><a href="/atom.xml" target="_blank" class="rss" title="rss"><i class="icon icon-lg icon-rss"></i></a></span>
            <span>博客内容遵循 <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p>
            <span>Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="http://fish_08.oschina.io/" target="_blank">yuliang</a></span>
            <span>尼古拉斯-鱼丸博客 &copy; 2015 - 2018</span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://fish_08.oschina.io/2017/07/04/redis/&title=《redis》 — 尼古拉斯-鱼丸博客&pic=http://fish_08.oschina.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://fish_08.oschina.io/2017/07/04/redis/&title=《redis》 — 尼古拉斯-鱼丸博客&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://fish_08.oschina.io/2017/07/04/redis/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《redis》 — 尼古拉斯-鱼丸博客&url=http://fish_08.oschina.io/2017/07/04/redis/&via=http://fish_08.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://fish_08.oschina.io/2017/07/04/redis/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/', SHARE: true, REWARD: true };



</script>

<script src="/js/main.min.js?v=1.6.7"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="/js/search.min.js?v=1.6.7" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>


</body>
</html>
